package DispatchSys;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;

import Shared.Utils;

/**
 * The Database module, see Architecture report for specifications.
 */

public class Database {

	// List of ambulances
	private Hashtable<Integer, AmbulanceInfo> ambs;

	// List of emergencies
	private List<ERInfo> ers;

	// List of assignments
	private List<Assignment> assignments;

	// THE database
	private static final Database instance = new Database();

	/**
	 * 
	 * @return the unique AssignmentManager instance.
	 */
	public static Database getInstance() {
		return instance;
	}

	/**
	 * Creates the database and initialize it
	 */
	private Database() {
		this.ambs = new Hashtable<Integer, AmbulanceInfo>();
		this.ers = new LinkedList<ERInfo>();
		this.assignments = new LinkedList<Assignment>();
	}

	/**
	 * @return the list of all free ambulances
	 */
	synchronized public List<AmbulanceInfo> getFreeAmbulances() {

		List<AmbulanceInfo> freeAmbs = new ArrayList<AmbulanceInfo>();

		for (AmbulanceInfo a : ambs.values()) {
			assert a != null;
			if (a.isFree())
				freeAmbs.add(a);
		}

		return freeAmbs;
	}

	/**
	 * @return the list of all free equipped ambulances
	 */
	synchronized public List<AmbulanceInfo> getFreeEquipedAmbulances() {

		List<AmbulanceInfo> freeAmbs = new ArrayList<AmbulanceInfo>();

		for (AmbulanceInfo a : ambs.values()) {
			assert a != null;
			if (a.isFree() && a.isEquiped())
				freeAmbs.add(a);
		}

		return freeAmbs;
	}

	/**
	 * @return the list of all ambulances registered in the system
	 */
	synchronized public List<AmbulanceInfo> getAllAmbulances() {

		List<AmbulanceInfo> ambsList = new ArrayList<AmbulanceInfo>();

		for (AmbulanceInfo a : ambs.values()) {
			assert a != null;
			ambsList.add(a);
		}

		return ambsList;

	}

	/**
	 * @return true if an equipped ambulance is available
	 */

	synchronized public boolean equipedAmbulanceAvailable() {

		for (AmbulanceInfo current : this.getFreeAmbulances()) {
			if (current != null && current.isEquiped())
				return true;
		}

		return false;

	}

	/**
	 * @return the list of all unassigned emergencies
	 */
	synchronized public List<ERInfo> getAllUnassignedER() {

		List<ERInfo> unAssignedER = new ArrayList<ERInfo>();

		for (ERInfo current : this.ers) {
			if (current.isUnAssigned())
				unAssignedER.add(current);
		}

		return unAssignedER;
	}

	/**
	 * @return the list of all emergencies ever recorded in the system
	 */
	synchronized public List<ERInfo> getAllER() {

		return new LinkedList<ERInfo>(ers);

	}

	/**
	 * @return the list of all assignments
	 */

	synchronized public List<Assignment> getAllAssignments() {

		return new LinkedList<Assignment>(assignments);

	}

	/**
	 * @return the list of all assigned emergencies
	 */
	synchronized public List<ERInfo> getAssignedER() {

		List<ERInfo> assignedER = new ArrayList<ERInfo>();

		for (ERInfo current : this.ers) {
			if (current.isAssigned())
				assignedER.add(current);
		}

		return assignedER;

	}

	/**
	 * Registers an emergency
	 * 
	 * @param er
	 *            the emergency to register != null
	 */
	synchronized public void addER(ERInfo er) {
		assert er != null;

		Utils.disp("ERInfo add : " + (er.isUrgent() ? "Urgent" : "Simple"));

		ers.add(er);
		Collections.sort(ers);
	}

	/**
	 * Removes specified emergency from the system
	 * 
	 * @param er
	 *            the emergency to remove != null
	 */
	synchronized public void remER(ERInfo er) {
		assert er != null;
		ers.remove(er);
	}

	/**
	 * Registers an ambulance
	 * 
	 * @param ai
	 *            the ambulance to register != null
	 */
	synchronized public void addAmb(AmbulanceInfo ai) {
		assert ai != null;

		Utils.disp("ambInfo add : " + (ai.isEquiped() ? "Equiped" : "Simple"));

		ambs.put(ai.getID(), ai);
	}

	/**
	 * Removes specified ambulance from the system
	 * 
	 * @param ai
	 *            the ambulance to remove != null
	 */
	synchronized public void remAmb(AmbulanceInfo ai) {
		assert ai != null;
		ambs.remove(ai.getID());
	}

	/**
	 * Removes ambulance with ID ambID from the system
	 * 
	 * @param ambID
	 *            the ambulance to remove != null
	 */
	synchronized public void remAmb(int ambID) {
		ambs.remove(ambID);
	}

	/**
	 * Registers an assignment
	 * 
	 * @param a
	 *            the assignment to register != null
	 */
	synchronized public void addAssignment(Assignment a) {
		assert a != null;
		assignments.add(a);
	}

	/**
	 * Removes specified assignment from the system
	 * 
	 * @param a
	 *            the assignment to remove != null
	 */
	synchronized public void remAssignment(Assignment a) {
		assert a != null;
		assignments.remove(a);
	}

	/**
	 * Returns the ambulance corresponding to an ID
	 * 
	 * @param ambID
	 *            the ID of the ambulance to fetch
	 * @return the AmbulanceInfo with ID equal to ambID; null if there is no
	 *         ambulances registered with ID equal to ambID
	 */
	synchronized public AmbulanceInfo getAmbulance(int ambID) {

		return ambs.get(ambID);
	}

	/**
	 * Removes every Ambulance, Emergency and Assignment from the database.
	 */
	synchronized public void removeAll() {
		this.ambs = new Hashtable<Integer, AmbulanceInfo>();
		this.ers = new ArrayList<ERInfo>();
		this.assignments = new ArrayList<Assignment>();
	}

}
